﻿using CocoaAni.Net.WebSockets.Exceptions;
using CocoaAni.Net.WebSockets.Server.Attributes;
using CocoaAni.Net.WebSockets.Server.Configs;
using CocoaAni.Net.WebSockets.Server.Controllers;
using CocoaAni.Net.WebSockets.Server.DefineInfos;
using CocoaAni.Net.WebSockets.Server.Exceptions;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using CocoaAni.Net.WebSockets.Server;
using CocoaAni.Net.WebSockets.Server.ArgAdapters.Base;
using CocoaAni.Net.WebSockets.Server.SendAdapters;

namespace CocoaAni.Net.WebSockets.Server;

public class WebSocketService
{
    internal int ClientIdCounter { get; set; } = 0;
    internal Socket? Listener { get; set; }
    internal WebSocketServiceConfig Config { get; set; }
    internal Dictionary<int, WebSocketContext> Contexts { get; set; } = null!;
    internal Dictionary<string, ControllerInfo> ControllerInfos { get; set; } = null!;
    public Dictionary<Type, IMessageArgAdapter> MessageArgAdapters { get; set; } = null!;
    public Dictionary<Type, IMessageSendAdapter> MessageSendAdapters { get; set; } = null!;

    public MessageHandlerArgsBuilder MessageHandlerArgsBuilder { get; set; } = null!;

    public WebSocketService(WebSocketServiceConfig config)
    {
        Config = config;
    }

    public WebSocketService(Action<WebSocketServiceConfig> initializer)
    {
        var config = new WebSocketServiceConfig();
        initializer.Invoke(config);
        Config = config;
        
    }

    public void Init()
    {
        CollectControllerInfos();
        CollectMessageArgAdapters();
        CollectMessageSendAdapters();
        Contexts = new Dictionary<int, WebSocketContext>();
        MessageHandlerArgsBuilder = new MessageHandlerArgsBuilder(MessageArgAdapters);
        Dispatcher = new WebSocketServiceDispatcher(Config,MessageHandlerArgsBuilder,MessageSendAdapters,ControllerInfos);
    }

    internal WebSocketServiceDispatcher Dispatcher { get; set; } = null!;

    public void CollectControllerInfos()
    {
        ControllerInfos = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(t => t.GetTypes())
            .Where(t => t.IsAssignableTo(typeof(WebSocketController)) && !t.IsAbstract &&
                        t.GetCustomAttribute<WebSocketControllerAttribute>() != null)
            .Select(t => new ControllerInfo(t)).ToDictionary(t => t.MapPath);
    }
    public void CollectMessageArgAdapters()
    {
        MessageArgAdapters = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(t => t.GetTypes())
            .Where(t => !t.IsAbstract && t.IsAssignableTo(typeof(IMessageArgAdapter)))
            .Select(t => (IMessageArgAdapter)Activator.CreateInstance(t)!)
            .ToDictionary(t => t.TargetAttributeType??t.TargetType);
    }
    public void CollectMessageSendAdapters()
    {
        MessageSendAdapters = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(t => t.GetTypes())
            .Where(t => !t.IsAbstract && t.IsAssignableTo(typeof(IMessageSendAdapter)))
            .Select(t => (IMessageSendAdapter)Activator.CreateInstance(t)!)
            .ToDictionary(t => t.TargetType);
    }

    public WebSocketService AddController<T>()
        where T : WebSocketController, new()
    {
        var t = typeof(T);
        if (t.IsAbstract)
        {
            throw new WebSocketControllerException($"ControllerInfo Is IsAbstract =>{t.FullName}", null!);
        }
        var attr = t.GetCustomAttribute<WebSocketControllerAttribute>();
        if (attr == null)
        {
            throw new WebSocketControllerException("Not Found [WebSocketControllerAttribute]", null!);
        }
        var controllerInfo = new ControllerInfo(t);
        ControllerInfos.Add(controllerInfo.MapPath, controllerInfo);
        return this;
    }
    public async Task RunAsync(CancellationToken cancellationToken = default)
    {
        if (MessageArgAdapters == null || ControllerInfos == null || MessageHandlerArgsBuilder==null || MessageSendAdapters==null)
        {
            throw new WebSocketServiceException("Please Invoke The Init First!");
        }
        var ipAddresses = await Dns.GetHostAddressesAsync(Config.Url.Host, cancellationToken);
        if (ipAddresses.Length == 0)
            throw new WebSocketListenerException($"不知道这样的主机地址：{Config.Url.Host}");
        Listener = new Socket(ipAddresses[0].AddressFamily, SocketType.Stream, ProtocolType.IP);
        Listener.Bind(new IPEndPoint(ipAddresses[0], Config.Url.Port));
        Listener.Listen(Config.ListenBacklog); ;
        while (true)
        {
            var socket = await Listener.AcceptAsync(cancellationToken);
            Dispatcher.StartDispatch(new WebSocket(socket), cancellationToken);
        }
    }
}